﻿using HlELock.Core.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace HlELock.Core
{
    public class UsbHid : IDisposable, IUsbHid
    {
        public UsbHid()
        {
            DeviceHandle = new IntPtr(-1);
        }
        public IntPtr DeviceHandle { get; internal set; }
        public string Path { get; set; }
        public ushort VID { get; set; }
        public ushort PID { get; set; }
        public ushort VER { get; set; }
        public string ProductName { get; set; }
        public string Manufacturer { get; set; }

        private Thread _receiveThread;
        private CancellationTokenSource _receiveThreadCancellationTokenSource;

        public void ClearEvents()
        {
            var invocationList = OnReceiveMessaged?.GetInvocationList();
            if (invocationList == null) return;
            foreach (var item in OnReceiveMessaged.GetInvocationList())
            {
                OnReceiveMessaged -= (Action<byte[]>)item;
            }
        }
        /// <summary>
        /// 接收消息事件
        /// </summary>
        public event Action<byte[]> OnReceiveMessaged;
        public void Beep()
        {
            ClearEvents();
            SendMessage("584B07504B0409");
        }
        public bool Test()
        {
            var deviceHandle = CH9326DLL.CH9326OpenDevicePath(Path);
            if (DeviceHandle == new IntPtr(-1))
            {
                return false;
            }
            // CH9326DLL.CH9326SetDeviceCfg(deviceHandle, 1027, 55555);
            return true;
        }
        public bool OpenWithPath()
        {
            DeviceHandle = CH9326DLL.CH9326OpenDevicePath(Path);
            return OpenDevice();
        }
        public bool Open()
        {
            Close();
            DeviceHandle = CH9326DLL.CH9326OpenDevice((ushort)MyAppContext.VID, (ushort)MyAppContext.PID);
            return OpenDevice();

        }
        private bool OpenDevice()
        {
            if (DeviceHandle == new IntPtr(-1))
            {
                return false;
            }
            CH9326DLL.CH9326SetTimeOut(DeviceHandle, 3000, 3000);
            _receiveThreadCancellationTokenSource = new CancellationTokenSource();
            _receiveThread = new Thread(ReceiveMessage)
            {
                IsBackground = true
            };
            _receiveThread.Start();
            Thread.Sleep(500);
            return true;
        }

        public byte[] SendMessage(string hexString, bool hasReturn = false)
        {
            var datas = Enumerable.Range(0, hexString.Length)
                     .Where(x => x % 2 == 0)
                     .Select(x => Convert.ToByte(hexString.Substring(x, 2), 16))
                     .ToArray();
            return SendMessage(datas, hasReturn);
        }
        private static object _lockObject = new object();
        public byte[] SendMessage(byte[] datas, bool hasReturn = false)
        {
            lock (_lockObject)
            {
                datas[2] = (byte)datas.Length;


                Console.WriteLine("发送指令:{0}", BitConverter.ToString(datas).Replace("-", " "));


                Thread.Sleep(200);
                if (DeviceHandle == new IntPtr(-1))
                {
                    throw new Exception("设备尚未连接");
                }

                byte[] report = new byte[64];
                IntPtr hEventObject = winapi.CreateEvent(IntPtr.Zero, true, true, "");
                byte len = (byte)datas.Length;
                Array.Copy(datas, report, datas.Length);
                var result = CH9326DLL.CH9326WriteData(DeviceHandle, report, len, hEventObject);
                winapi.CloseHandle(hEventObject);
                byte[] receiveData = Array.Empty<byte>();
                if (hasReturn)
                {
                    var eventToken = EventBus.Instace.GetEvent<UsbHidOnReceivedEvent>().Subscribe(d =>
                     {
                         Volatile.Write(ref receiveData, d.Datas);
                     });
                    var index = 0;
                    while (index < 100 && receiveData.Length == 0)
                    {
                        index++;
                        Thread.Sleep(100);
                    }
                    EventBus.Instace.GetEvent<UsbHidOnReceivedEvent>().Unsubscribe(eventToken);
                    return receiveData;
                }
                return Array.Empty<byte>();

            }
        }
        private void ReceiveMessage()
        {
            byte[] receiveDataBuf = new byte[1024];
            uint len;//接受缓冲区大小  

            if (DeviceHandle == new IntPtr(-1))
            {
                throw new Exception("设备尚未连接");
            }
            if (CH9326DLL.CH9326InitThreadData(DeviceHandle) == 0)
            {
                throw new Exception("CH9326InitThreadData 错误");
            }
            try
            {
                //uint m_uRecvED = 0;//接收数据大小

                while (true)//取数据
                {
                    len = Convert.ToUInt32(receiveDataBuf.Length);
                    if (CH9326DLL.CH9326ReadThreadData(DeviceHandle, receiveDataBuf, ref len) == 0)
                    {
                        break;
                    }

                    if (len != 0)
                    {
                        //var receviceData = BitConverter.ToString(receiveDataBuf).Replace("-", "");
                     //   m_uRecvED += len;//已接收数据大小
                        var data = new byte[len];
                        Array.Copy(receiveDataBuf, data, data.Length);
                        OnReceiveMessaged?.Invoke(data);
                        try
                        {
                            EventBus.Instace.GetEvent<UsbHidOnReceivedEvent>().Publish(new UsbHidOnReceivedEventArgs { Datas = data });
                        }
                        finally
                        {
                            CH9326DLL.CH9326ClearThreadData(DeviceHandle);
                        }
                    }
                }
            }
            catch (OperationCanceledException) when (_receiveThreadCancellationTokenSource.IsCancellationRequested)
            {
            }
            finally
            {
                CH9326DLL.CH9326StopThread(DeviceHandle);
            }
        }
        public void Close()
        {
            if (DeviceHandle != new IntPtr(-1))
            {
                try
                {
                    _receiveThreadCancellationTokenSource?.Cancel();
                    _receiveThread.Abort();
                    CH9326DLL.CH9326CloseDevice(DeviceHandle);
                }
                catch (Exception)
                {
                }
            }
        }
        public void Dispose()
        {
            Close();
        }

        public override string ToString()
        {
            return $"{ProductName} ({Manufacturer})  VID={VID} PID={PID} VER={VER}";
        }

        public bool RewriteInfo()
        {

            if (DeviceHandle == new IntPtr(-1))
            {
                this.OpenWithPath();

            }
            ushort vid = 1027;
            ushort pid = 55555;
            var manufacturer = System.Text.Encoding.Unicode.GetBytes("Hlock");
            var product = System.Text.Encoding.Unicode.GetBytes("Lock Device");
            var serialNo = System.Text.Encoding.Unicode.GetBytes("11111111");
            var rst = CH9326DLL.CH9326SetDeviceCfg(DeviceHandle,
                  vid,
                  pid,
                  80,
                  manufacturer,
                 10,
                product,
                20,
                serialNo, 20);
            return rst > 0;
        }
    }
}
